package com.jessefyz.module.service.impl;

import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.binarywang.wx.miniapp.api.WxMaUserService;
import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult;
import cn.binarywang.wx.miniapp.bean.WxMaPhoneNumberInfo;
import cn.binarywang.wx.miniapp.bean.WxMaUserInfo;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.jessefyz.common.core.domain.AjaxResult;
import com.jessefyz.common.exception.CustomException;
import com.jessefyz.common.exception.DataExistException;
import com.jessefyz.common.exception.DataNotFoundException;
import com.jessefyz.common.exception.ParamException;
import com.jessefyz.common.utils.*;
import com.jessefyz.common.utils.ip.IpUtils;
import com.jessefyz.common.utils.sign.Md5Utils;
import com.jessefyz.module.dto.*;
import com.jessefyz.module.member.domain.MemberInfo;
import com.jessefyz.module.member.mapper.MemberInfoMapper;
import com.jessefyz.module.req.MiniAppLoginReq;
import com.jessefyz.module.service.CaptchaCodeManager;
import com.jessefyz.module.service.CouponAssignService;
import com.jessefyz.module.service.IApiLoginService;
import com.jessefyz.module.service.TokenService;
import com.jessefyz.module.shop.notify.NotifyService;
import com.jessefyz.module.shop.notify.NotifyType;
import com.jessefyz.module.vo.MemberVo;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Member;
import java.time.LocalDateTime;
import java.util.*;

/**
 * loginService 服务层实现
 */
@Service
public class ApiLoginServiceImpl extends ServiceImpl<MemberInfoMapper, MemberInfo> implements IApiLoginService {

    @Autowired
    private TokenService tokenService;

    @Autowired
    private WxMaService wxService;

    @Autowired
    private NotifyService notifyService;

    @Autowired
    private CouponAssignService couponAssignService;

    /**
     * @Description 小程序登录
     * @Author Jesse
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public AjaxResult<MemberVo> loginBywechatMiniApp(WxLoginInfo wxLoginInfo, HttpServletRequest request) throws Exception {
        String code = wxLoginInfo.getCode();
        UserInfo userInfo = wxLoginInfo.getUserInfo();
        if (null == userInfo) {
            throw new ParamException("userInfo参数不能为空");
        }

        String sessionKey = null;
        String openId = null;
        String unionId = null;
        try {
            WxMaJscode2SessionResult result = this.wxService.getUserService().getSessionInfo(code);
            sessionKey = result.getSessionKey();
            openId = result.getOpenid();
            unionId = result.getUnionid();
        } catch (Exception e) {
            e.printStackTrace();
        }

        if (sessionKey == null || openId == null) {
            throw new CustomException("openId获取异常");
        }
        MemberInfo member =  this.getOne(new LambdaQueryWrapper<MemberInfo>()
                        .eq(MemberInfo::getWxMiniOpenId,openId)
                ,false);
        if (member == null) {
            //新会员
            member = MemberInfo.builder()
                    .source(1)
                    .sex((com.jessefyz.common.utils.StringUtils.isBlank(userInfo.getGender())?0:Integer.valueOf(userInfo.getGender())))
                    .nickname(SLEmojiFilter.filterEmoji(userInfo.getNickName()))
                    .name(SLEmojiFilter.filterEmoji(userInfo.getNickName()))
                    .pic(userInfo.getAvatarUrl())
                    .wxUnionId(unionId)
                    .wxMiniOpenId(openId)
                    .lastLoginTime(new Date())
                    .lastLoginIp(IpUtils.getIpAddr(request))
                    .sessionKey(sessionKey)
                    .userLevel(0)
                    .build();
            this.save(member);
            // 新用户发送注册优惠券
            couponAssignService.assignForRegister(member.getId());
        } else {
            member.setLastLoginTime(new Date());
            member.setLastLoginIp(IpUtils.getIpAddr(request));
            member.setSessionKey(sessionKey);
            if (!this.updateById(member)) {
                throw new CustomException("操作异常");
            }
        }
        member = this.getById(member.getId());
        MemberVo memberVo= getMemberVo(member);
        return AjaxResult.success(memberVo);
    }

    /**
     * @Description 账号密码登录
     * @Author Jesse
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public  AjaxResult<MemberVo> loginByAccount(AccountLoginInfo accountLoginInfo, HttpServletRequest request) {
        MemberInfo memberInfo = Optional.ofNullable(this.getOne(new LambdaQueryWrapper<MemberInfo>()
            .eq(MemberInfo::getUsername,accountLoginInfo.getUsername())
        ,false)).orElseThrow(()-> new DataNotFoundException("账号或密码错误"));

        if (!accountLoginInfo.getPassword().equals(Md5Utils.hash(accountLoginInfo.getPassword()))) {
            throw new DataNotFoundException("账号或密码错误");
        }

        // 更新登录情况
        memberInfo.setLastLoginTime(new Date());
        memberInfo.setLastLoginIp(IpUtils.getIpAddr(request));
        if (!this.updateById(memberInfo)) {
            throw new CustomException("登录失败，请联系管理员");
        }
        //添加或修改会员信息
        memberInfo = this.getById(memberInfo.getId());
        MemberVo memberVo= getMemberVo(memberInfo);
        return AjaxResult.success(memberVo);
    }

    /**
     * @Description 发送手机验证码
     * @Author Jesse
     */
    @Override
    public  void registerCaptcha(RegisterCaptcha registerCaptcha) {

        if (!notifyService.isSmsEnable()) {
            throw new CustomException("小程序后台验证码服务不支持");
        }
        String code = CharUtil.getRandomNum(6);
        boolean successful = CaptchaCodeManager.addToCache(registerCaptcha.getMobile(), code);
        if (!successful) {
            throw new CustomException("验证码未超时1分钟，不能发送");
        }
        notifyService.notifySmsTemplate(registerCaptcha.getMobile(), NotifyType.CAPTCHA, new String[]{code});
    }

    /**
     * @Description 账号密码注册
     * @Author Jesse
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public AjaxResult<MemberVo> register(Register register, HttpServletRequest request) {

        MemberInfo memberInfo = this.getOne(new LambdaQueryWrapper<MemberInfo>()
                        .eq(MemberInfo::getUsername,register.getUsername())
                ,false);
        if (null != memberInfo) {
            throw new DataExistException("用户名已注册");
        }
        if (!RegexUtil.isMobileSimple(register.getMobile())) {
            throw new ParamException("手机号格式不正确");
        }
        memberInfo = this.getOne(new LambdaQueryWrapper<MemberInfo>()
                        .eq(MemberInfo::getMobile,register.getMobile())
                ,false);
        if (null != memberInfo) {
            throw new DataExistException("手机号码已注册");
        }
        //判断验证码是否正确
        String cacheCode = CaptchaCodeManager.getCachedCaptcha(register.getMobile());
        if (cacheCode == null || cacheCode.isEmpty() || !cacheCode.equals(register.getCode())) {
            throw new ParamException("验证码错误");
        }
        memberInfo = new MemberInfo();
        String openId = "";
        // 非空，则是小程序注册
        // 继续验证openid
        String sessionKey = "";
        if(!org.springframework.util.StringUtils.isEmpty(register.getWxCode())) {
            try {
                WxMaJscode2SessionResult result = this.wxService.getUserService().getSessionInfo(register.getWxCode());
                openId = result.getOpenid();
                sessionKey = result.getSessionKey();
            } catch (Exception e) {
                e.printStackTrace();
                throw new ParamException("openid 获取失败");
            }
            MemberInfo memberWx=  this.getOne(new LambdaQueryWrapper<MemberInfo>()
                            .eq(MemberInfo::getWxMiniOpenId,openId)
                    ,false);
            if (null != memberWx) {
                //如果有手机号码，并且手机号码对应不上
                if (StringUtils.isNotBlank(memberWx.getMobile()) && !memberWx.getMobile().equals(register.getMobile())) {
                    throw new CustomException("该微信用户已经绑定其他手机号码，不可绑定");
                }
                if (StringUtils.isNotBlank(memberWx.getUsername())) {
                    throw new CustomException("该微信用户已经绑定其他账号，不可绑定");
                }
                memberInfo = memberWx;
            }
            memberInfo.setSessionKey(sessionKey);
            memberInfo.setWxMiniOpenId(openId);
        }

        memberInfo.setUsername(register.getUsername());
        memberInfo.setPassword(Md5Utils.hash(register.getPassword()));
        memberInfo.setMobile(register.getMobile());
        memberInfo.setNickname(register.getUsername());
        memberInfo.setPic("https://yanxuan.nosdn.127.net/80841d741d7fa3073e0ae27bf487339f.jpg?imageView&quality=90&thumbnail=64x64");
        memberInfo.setUserLevel(0);
        memberInfo.setSource(2);//账号密码注册
        memberInfo.setLastLoginTime(new Date());
        memberInfo.setLastLoginIp(IpUtils.getIpAddr(request));
        this.saveOrUpdate(memberInfo);
        if (null == memberInfo.getId()) {
            this.save(memberInfo);
            // 给新用户发送注册优惠券
            couponAssignService.assignForRegister(memberInfo.getId());
        } else {
            this.updateById(memberInfo);
        }
        memberInfo = this.getById(memberInfo.getId());
        MemberVo memberVo= getMemberVo(memberInfo);
        return AjaxResult.success(memberVo);
    }


    /**
     * @Description 发送验证码
     * @Author Jesse
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void captcha(Captcha captcha) {

        if (!notifyService.isSmsEnable()) {
            throw new CustomException("小程序后台验证码服务不支持");
        }
        String code = CharUtil.getRandomNum(6);
        boolean successful = CaptchaCodeManager.addToCache(captcha.getMobile(), code);
        if (!successful) {
            throw new CustomException("验证码未超时1分钟，不能发送");
        }
        notifyService.notifySmsTemplate(captcha.getMobile(), NotifyType.CAPTCHA, new String[]{code});

    }

    /**
     * @Description 重置账号密码
     * @Author Jesse
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void reset( ResetPassword resetPassword) {
        //判断验证码是否正确
        String cacheCode = CaptchaCodeManager.getCachedCaptcha(resetPassword.getMobile());
        if (cacheCode == null || cacheCode.isEmpty() || !cacheCode.equals(resetPassword.getCode()))
            throw new ParamException("验证码错误");

        List<MemberInfo> memberInfos = list(new LambdaQueryWrapper<MemberInfo>()
            .eq(MemberInfo::getMobile,resetPassword.getMobile())
        );
        MemberInfo memberInfo = null;
        if (memberInfos.size() > 1) {
            throw new DataExistException("手机号异常");
        } else if (memberInfos.size() == 0) {
            throw new DataNotFoundException("手机号未注册");
        } else {
            memberInfo = memberInfos.get(0);
        }
        memberInfo.setPassword(Md5Utils.hash(resetPassword.getPassword()));

        if (!updateById(memberInfo)) {
            throw new CustomException("reset异常");
        }
    }

    /**
     * @Description 重置手机号密码
     * @Author Jesse
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void resetPhone(MemberInfo currentMember, ResetPassword resetPassword) {
        //判断验证码是否正确
        String cacheCode = CaptchaCodeManager.getCachedCaptcha(resetPassword.getMobile());
        if (cacheCode == null || cacheCode.isEmpty() || !cacheCode.equals(resetPassword.getCode()))
            throw new CustomException("验证码错误");

        if (this.count(new LambdaQueryWrapper<MemberInfo>()
            .eq(MemberInfo::getMobile,resetPassword.getMobile())
            .ne(MemberInfo::getId,currentMember.getId())
        ) > 0) {
            throw new CustomException("手机号已注册");
        }
        currentMember = this.getById(currentMember.getId());

        if (!currentMember.getPassword().equals(Md5Utils.hash(resetPassword.getPassword()))) {
            throw new CustomException("账号密码不对");
        }
        currentMember.setMobile(resetPassword.getMobile());
        if (!this.updateById(currentMember)) {
            throw new CustomException("操作失败，请联系管理员");
        }
    }


    /**
     * @Description 账号信息更新
     * @Author Jesse
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void profile(MemberInfo currentMember, ProfileUpdate profileUpdate) {
       BeanUtils.copyProperties(profileUpdate,currentMember);
        if (!this.updateById(currentMember)) {
            throw new CustomException("操作失败，请联系管理员");
        }

    }

    /**
     * @Description 绑定微信手机号
     * @Author Jesse
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void bindPhone(MemberInfo currentMember, WxBindPhone wxBindPhone) {
        currentMember = this.getById(currentMember.getId());
        WxMaPhoneNumberInfo phoneNumberInfo = this.wxService.getUserService().getPhoneNoInfo(currentMember.getSessionKey(),
                wxBindPhone.getEncryptedData(),wxBindPhone.getIv());
        String phone = phoneNumberInfo.getPhoneNumber();
        currentMember.setMobile(phone);
        if (!this.updateById(currentMember)) {
            throw new CustomException("操作失败，请联系管理员");
        }
    }

    /**
     * 生成memberVO
     * @param memberInfo
     * @return
     */
    @Override
    public MemberVo getMemberVo(MemberInfo memberInfo) {
        MemberVo memberVo = new MemberVo();
        BeanUtils.copyProperties(memberInfo,memberVo);
        String token = tokenService.createToken(memberInfo);
        memberVo.setToken("Bearer ".concat(token));
        return memberVo;
    }
}
